package com.deruan.hotcore.resource.table;

import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Observable;
import java.util.logging.Logger;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.TreePath;

import org.columba.api.gui.frame.IFrameMediator;
import org.columba.core.folder.api.IFolder;
import org.columba.core.folder.api.IFolderCommandReference;
import org.columba.core.gui.base.AscendingIcon;
import org.columba.core.gui.base.DescendingIcon;
import org.columba.core.gui.menu.ExtendablePopupMenu;
import org.columba.core.gui.menu.MenuXMLDecoder;
import org.columba.core.io.DiskIO;
import org.columba.mail.folder.IMailFolder;
import org.columba.mail.folder.IMailbox;
import org.columba.mail.folderoptions.FolderOptionsController;
import org.columba.mail.gui.frame.MailFrameMediator;
import org.columba.mail.gui.table.IMessageNode;
import org.columba.mail.gui.table.SortingStateObservable;
import org.columba.mail.gui.table.TableViewTransferHandler;
import org.columba.mail.gui.table.model.HeaderTableModel;
import org.columba.mail.gui.table.model.MessageNode;
import org.columba.mail.gui.table.model.TableModelChangedEvent;
import org.columba.mail.gui.table.model.TableModelSorter;
import org.columba.mail.message.IColumbaHeader;
import org.frapuccino.treetable.Tree;

import com.deruan.hotcore.base.common.bean.ResourceBean;
import com.deruan.hotcore.resource.gui.tree.ResourceTreeNode;
import com.deruan.hotcore.resource.table.model.IResourceHeaderTableModel;
import com.deruan.hotcore.resource.table.model.ResourceHeaderTableModel;
import com.deruan.hotcore.resource.table.model.ResourceTableModelChangedEvent;
import com.deruan.hotcore.resource.table.model.ResourceTableModelChangedListener;
import com.deruan.hotcore.resource.table.model.ResourceTableModelSorter;

public class ResourceTableController implements ListSelectionListener,ResourceTableModelChangedListener, IResourceTableController
{
	 /** JDK 1.4+ logging framework logger, used for logging. */
	  private static final Logger LOG = Logger.getLogger("org.columba.mail.gui.table");

	  private ResourceHeaderTableModel headerTableModel;

	  protected ResourceTableView view;

	  protected IFrameMediator frameController;

	  protected ExtendablePopupMenu menu;
	  
	  protected ExtendablePopupMenu resourceMenu;

	  protected ResourceTableModelSorter tableModelSorter;

	  /**
	   * previously selected rows
	   */
	  protected int[] previouslySelectedRows;

	  private MessageNode[] previouslySelectedNodes;

	  /**
	   * previously selected folder
	   */
	  private IMailbox previouslySelectedFolder;
	  
	  public ResourceTableController(IFrameMediator frameController)
	  {
		  this.frameController = frameController;
		    // init table model
		    headerTableModel = new ResourceHeaderTableModel();
		    	    
            //init sorting model
		    tableModelSorter = new ResourceTableModelSorter();
		    headerTableModel.registerVisitor(tableModelSorter);

            //init view
		    view = new ResourceTableView(headerTableModel, tableModelSorter);
		    // pass tree to model, used by the threaded-view
		    headerTableModel.setTree((Tree) view.getTree());

		    getView().setTransferHandler(new TableViewTransferHandler(getFrameController()));
		    getView().setDragEnabled(true);

		    // MouseListener sorts table when clicking on a column header
		    new TableHeaderMouseListener(getTableModelSorter());
		    view.getColumnModel().addColumnModelListener(headerTableModel);

		    // we need this for the focus manager
		    getView().getSelectionModel().addListSelectionListener(this);

		    JTableHeader header = view.getTableHeader();
	  }
	  
	  public IFrameMediator getFrameController() 
	  {
		    return frameController;
	  }
	  
	  public IResourceHeaderTableModel getHeaderTableModel() 
	  {
		    return headerTableModel;
	  }
	  
	  public void showResourceList(IMailFolder folder, List<ResourceBean> headerList) throws Exception 
	  {
		    // save previously selected folder options
		    if (previouslySelectedFolder != null) 
		    {
		      ((MailFrameMediator) getFrameController()).getFolderOptionsController().save(previouslySelectedFolder);
		    }
		    // load options of newly selected folder
		    ((MailFrameMediator) getFrameController()).getFolderOptionsController().load(FolderOptionsController.STATE_BEFORE);
		    
		    getHeaderTableModel().set(headerList);

		    // load options of newly selected folder
		    ((MailFrameMediator) getFrameController()).getFolderOptionsController().load(FolderOptionsController.STATE_AFTER);

		    // remember previously selected folder
		    previouslySelectedFolder = (IMailbox)folder;
	   }
	  
	  public JPopupMenu getResourcePopupMenu() 
	  {
		    return resourceMenu;
	  }
	  
	  public void tableChanged(ResourceTableModelChangedEvent event) 
	  {
		    // selected rows before updating the model
		    // -> used later to restore the selection
		    previouslySelectedRows = view.getSelectedRows();
		    previouslySelectedNodes = getView().getSelectedNodes();

		    // folder in which the update occurs
		    IFolder folder = event.getSrcFolder();

		    if (folder == null) 
		    {
		      return;
		    }

		    LOG.info("source folder=" + folder.getName());

		    // get current selection
		    IFolderCommandReference r = (IFolderCommandReference) ((MailFrameMediator) frameController).getTableSelection();
		    IFolder srcFolder = r.getSourceFolder();

		    // its always possible that no folder is currenlty selected
		    if (srcFolder != null) 
		    {
		      LOG.info("selected folder=" + srcFolder.getName());
		    }
		    // only update table if, this folder is the same
		    // as the currently selected
		    if (!folder.equals(srcFolder)) {
		      return;
		    }

		    switch (event.getEventType()) {
		    case -1: 
		    {
		      getHeaderTableModel().set(event.getHeaderList());
		      break;
		    }

		    case TableModelChangedEvent.UPDATE: 
		    {   	
		      getHeaderTableModel().update();
		      break;
		    }

		    case TableModelChangedEvent.REMOVE: 
		    {
		      getHeaderTableModel().remove(event.getUids());
		      break;
		    }

		    case TableModelChangedEvent.MARK: 
		    {
		      getHeaderTableModel().modify(event.getUids());
		      break;
		    }
		    }

		    // when marking messages, don't touch selection
		    if (event.getEventType() == TableModelChangedEvent.MARK)
		      return;

		    // re-select previous selection
		    if (previouslySelectedRows != null) {
		      // only re-select if only a single row was formerly selected
		      if ((previouslySelectedRows.length == 1) && (previouslySelectedNodes.length > 0)) {
		        int row = ((HeaderTableModel) getHeaderTableModel()).getRow(previouslySelectedNodes[0]);

		        // if message was removed from JTable
		        if (row == -1)
		          row = previouslySelectedRows[0];

		        // select row
		        view.selectRow(row);

		        // scrolling to the selected row
		        getView().makeRowVisible(row);

		      }
		    }

		  }
	  
	  public ResourceTableView getView() 
	  {
		    return view;
	  }
		  /**
		   * Select messages with UIDs.
		   * <p>
		   * Message UIDs are converted to {@link MessageNode}objects.
		   * 
		   * @param uids
		   *          array of message UIDs
		   */
		  public void setSelected(Object[] uids) {

		    // select nodes
		    MessageNode[] nodes = new MessageNode[uids.length];

		    for (int i = 0; i < uids.length; i++) {
		      nodes[i] = ((HeaderTableModel) getHeaderTableModel()).getMessageNode(uids[i]);
		    }

		    int[] rows = new int[nodes.length];
		    for (int i = 0; i < nodes.length; i++) {
		      TreePath path = new TreePath(nodes[i].getPath());
		      rows[i] = view.getTree().getRowForPath(path);

		    }
		    view.selectRow(rows[0]);

		  }

		  /** ************************ actions ******************************* */
		  /**
		   * create the PopupMenu
		   */
		  public void createResourcePopupMenu() {
		    try 
		    {
		      InputStream is = DiskIO.getResourceStream("org/columba/mail/action/resourcetable_contextmenu.xml");

		      resourceMenu = new MenuXMLDecoder(getFrameController()).createPopupMenu(is);
		    } catch (IOException e) {
		      e.printStackTrace();
		    }
		  }

		  /**
		   * Show empty messagelist with no elements.
		   */
		  public void clear() {
		    // clear model
		    getHeaderTableModel().set(null);

		  }

		  /** *********** implement getter/setter methods ******************** */
		  /**
		   * return the table model sorter
		   */
		  public ResourceTableModelSorter getTableModelSorter() 
		  {
		    return tableModelSorter;
		  }

		  /** ************************* ListSelectionListener interface ************* */

		  /**
		   * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
		   */
		  public void valueChanged(ListSelectionEvent arg0) 
		  {}

		  /**
		   * @see org.columba.mail.gui.table.model.TableModelChangedListener#isInterestedIn(IFolder)
		   */
		  public boolean isInterestedIn(IMailFolder folder) {

		    return folder == previouslySelectedFolder;
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getSelectedNodes()
		   */
		  public IMessageNode[] getSelectedNodes() {
		    return getView().getSelectedNodes();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#selectFirstRow()
		   */
		  public Object selectFirstRow() {
		    Object result = getView().selectFirstRow();

		    return result;
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#selectLastRow()
		   */
		  public Object selectLastRow() {
		    if (getView().getColumnCount() > 0) {
		      Object result = getView().selectLastRow();
		      return result;
		    } else {
		      return null;
		    }

		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#selectRow(int)
		   */
		  public void selectRow(int row) {
		    getView().selectRow(row);

		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getMessagNode(java.lang.Object)
		   */
		  public ResourceTreeNode getMessageNode(Object uid) {
		    return getView().getMessagNode(uid);
		  }
		  
		  /**
		   * @see org.columba.mail.gui.table.ITableController#clearSelection()
		   */
		  public void clearSelection() 
		  {
		     getView().getSelectionModel().clearSelection();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#makeSelectedRow
		   */
		  public void makeSelectedRowVisible() {
		    getView().scrollRectToVisible(getView().getCellRect(getView().getSelectedRow(), 0, false));
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getSortingStateObservable()
		   */
		  public Observable getSortingStateObservable() {
		    return getTableModelSorter().getSortingStateObservable();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#setSortingOrder(boolean)
		   */
		  public void setSortingOrder(boolean order) {
		    getTableModelSorter().setSortingOrder(order);
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#setSortingColumn(java.lang.String)
		   */
		  public void setSortingColumn(String column) {
		    getTableModelSorter().setSortingColumn(column);
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getSortingColumn()
		   */
		  public String getSortingColumn() {
		    return getTableModelSorter().getSortingColumn();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getSortingOrder()
		   */
		  public boolean getSortingOrder() {
		    return getTableModelSorter().getSortingOrder();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getRowCount()
		   */
		  public int getRowCount() {
		    return getView().getRowCount();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getColumnModel()
		   */
		  public TableColumnModel getColumnModel() {
		    return view.getColumnModel();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#resetColumnModel()
		   */
		  public void resetColumnModel() 
		  {
		    ResourceTableView view = getView();

		    // remove all columns from table model
		    getHeaderTableModel().clearColumns();

		    // reset row height
		    view.resetRowHeight();
		    view.setShowHorizontalLines(false);

		    // remove all columns for column model
		    view.getColumnModel().removeColumnModelListener(getHeaderTableModel());
		    view.setColumnModel(new DefaultTableColumnModel());
		    view.getColumnModel().addColumnModelListener(getHeaderTableModel());

		    // for some weird reason the table loses its inter-cell spacing
		    // property, when changing the underlying column model
		    // -> setting this to (0,0) again
		    view.setIntercellSpacing(new Dimension(0, 0));
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#createColumn(java.lang.String, int)
		   */
		  public TableColumn createTableColumn(String name, int size) {
		    return view.createTableColumn(name, size);
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#addColumn(javax.swing.table.TableColumn)
		   */
		  public void addColumn(TableColumn column) {
		    view.addColumn(column);
		  }

		  /**
		   * Mouse listener for selecting columns with the left mouse to change the sorting order.
		   * <p>
		   * Also responsible for changing the icon in the renderer
		   * 
		   * @author fdietz
		   */
		  class TableHeaderMouseListener extends MouseAdapter {

		    private ResourceTableModelSorter sorter;

		    private SortingStateObservable observable;

		    private ImageIcon ascending = new AscendingIcon();

		    private ImageIcon descending = new DescendingIcon();

		    /**
		     *  
		     */
		    public TableHeaderMouseListener(ResourceTableModelSorter sorter) {

		      this.sorter = sorter;

		      this.observable = sorter.getSortingStateObservable();

		      JTableHeader th = view.getTableHeader();
		      th.addMouseListener(this);
		    }

		    public void mouseClicked(MouseEvent e) {
		      TableColumnModel columnModel = view.getColumnModel();
		      int viewColumn = columnModel.getColumnIndexAtX(e.getX());
		      int column = viewColumn;

		      // int column = view.convertColumnIndexToModel(viewColumn);
		      // int column2 = view.convertColumnIndexToView(viewColumn);
		      if (column != -1) {
		        ImageIcon icon = null;

		        if (sorter.getSortingOrder() == true) {
		          icon = ascending;
		        } else {
		          icon = descending;
		        }

		        // disable every icon
		        // -> set appropriate icon for selected column
		        for (int i = 0; i < columnModel.getColumnCount(); i++) {
		          JLabel renderer = (JLabel) columnModel.getColumn(i).getHeaderRenderer();

		          if (i == column) {
		            renderer.setIcon(icon);
		          } else {
		            renderer.setIcon(null);
		          }
		        }

		        // remember selected node
		        MessageNode[] nodes = view.getSelectedNodes();
		        Object uid = null;

		        if ((nodes != null) && (nodes.length > 0))
		          uid = nodes[0].getUid();

		        // repaint table header
		        SwingUtilities.invokeLater(new Runnable() {
		          public void run() {
		            view.getTableHeader().repaint();
		          }
		        });

		        String columnName = getHeaderTableModel().getColumnName(column);

		        // notify the model to sort the table
		        // sorter.sort(column);
		        boolean order = false;

		        if (sorter.getSortingColumn().equals(columnName)) {
		          order = !sorter.getSortingOrder();
		        }
		        // notify observers (sorting state submenu)
		        observable.setSortingState(columnName, order);

		        setSortingColumn(columnName);
		        setSortingOrder(order);
		        getHeaderTableModel().update();

		        // make selected row visible again
		        if (uid != null)
		          setSelected(new Object[] { uid });

		      }
		    }
		  }

		  public ListSelectionModel getListSelectionModel() {
		    return view.getSelectionModel();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getSelectedRows()
		   */
		  public int[] getSelectedRows() {
		    return view.getSelectedRows();
		  }

		  /**
		   * @see org.columba.mail.gui.table.ITableController#getPathForRow(int)
		   */
		  public TreePath getPathForRow(int row) 
		  {
		    return view.getTree().getPathForRow(row);
		  } 
}
